package com.blackfish.api;

import com.blackfish.bag.BagService;
import com.blackfish.common.plugin.ClientHelper;
import com.blackfish.info.InfoService;
import com.blackfish.module.ModuleService;
import com.blackfish.path.PathService;
import com.blackfish.request.RequestService;
import com.blackfish.style.StyleService;
import com.blackfish.type.TypeService;
import com.blackfish.common.model.*;
import com.blackfish.customer.CustomerService;
import com.blackfish.command.*;
import com.blackfish.active.*;

import java.io.File;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import com.jfinal.plugin.activerecord.Record;
import com.jfinal.plugin.redis.Cache;
import com.jfinal.plugin.redis.Redis;
import org.json.JSONObject;
import org.w3c.dom.Document;
import org.w3c.dom.Element;


public class ApiService {
    private static final BagService bagService = new BagService();
    private static final ModuleService moduleService = new ModuleService();
    private static final StyleService styleService = new StyleService();
    private static final CustomerService customerService = new CustomerService();
    private static final PathService pathService = new PathService();
    private static final CommandService comService = new CommandService();
    private static final TypeService typeService = new TypeService();
    private static final ActiveService activeService = new ActiveService();
    private static final RequestService requestService = new RequestService();
    private static InfoService infoService = new InfoService();

    private static Map<String, String> reqMap = new HashMap<String, String>();
    private static List<String> mapList = new ArrayList<String>();
    private static String reqLock = null;
    private static Map<String, String> lostMap = new HashMap<String, String>(); //没人认领的事件会被放在这里
    private static int errorTime = 0;

    private static ClientHelper clientHelper = new ClientHelper();

    private String eventJson(int id, String active, String data, String time,String bagPath) {
        JSONObject obj = new JSONObject();
        obj.put("id", id);
        obj.put("name", moduleService.findById(id).getTitle());
        obj.put("data", data);
        obj.put("time", time);
        obj.put("active", active);
        obj.put("bagPath", bagPath);
        obj.put("bag", bagService.findById(moduleService.findById(id).getBag()).getName());
        obj.put("style", styleService.findById(moduleService.findById(id).getStyle()).getName());

        return obj.toString();
    }

    public String getEventList(String key) {
        if (mapList.isEmpty()) {
            return null;
        }
        //屏蔽掉这一选项，是为了防止，当客户端没有接收到，或者生成事件时发生意外后再次访问而访问不到内容，从而卡死
        //以后应该建立意外锁，当多个客户端访问时，有一位拿到锁但是突然掉线，就会全体宕机，
        //所以需要一个异步接口进行解锁
        //所有客户端都可拿到钥匙，但是主钥匙没人访问或者访问过的就可以拿这一把解锁
        //但是怎么让客户端知道自己拿的是副钥匙要继续思考
        else {
            if (reqLock != null) {
                return reqMap.get(mapList.get(0));//json可以随便拿只要你有钥匙
            }
            reqLock = mapList.get(0) + key;
            return reqMap.get(mapList.get(0));
        }
    }

    //将现在的时间作为key值，保存为list的值和map的键，map的值为所得到的json，
    //json中包含了passtime，但是要想和服务端匹配，要知道客户端请求时发送的内容，两个相互匹配才能使用

    // TODO 向Redis中添加事件
    public boolean addEventList(int id, String active, String data,List<String> deviceKeys,String bagPath) throws Exception {
        String time = java.time.LocalTime.now().toString();
        String json = eventJson(id, active, data, time,bagPath);
        Cache eventCache = com.jfinal.plugin.redis.Redis.use("event");
        //eventCache.sadd(time,json);

        // TODO 通过控件id获得设备id
        //List<Record> devices = infoService.getDidByMid(id);
        for (String deviceKey : deviceKeys) {
            String device_id = deviceKey;
            eventCache.lpush(device_id, json);
            eventCache.lpush("eventlist", device_id);
            List<Object> device_detail = eventCache.lrange("id:" + device_id, 0, -1);

            String imIp = (String)device_detail.get(2);
            return clientHelper.sendImMessage(device_id, json, imIp);
        }

        return false;
    }

//    public boolean addEventList(int id, String active, String data, boolean Redis) {
//        String time = java.time.LocalTime.now().toString();
//        reqMap.put(time, eventJson(id, active, data, time));
//        mapList.add(time);
//        return false;
//    }

    public boolean delEventlist(String passKey) {
        Cache eventCache = Redis.use("event");
        eventCache.lrem("eventlist",1,passKey);

        // TODO 万一不是第一个怎么办
        eventCache.lpop(passKey);
        return false;
    }

//    public boolean delEventlist(String passKey) {
//        if (reqLock.equals(passKey)) {
//            String rmKey = mapList.remove(0);
//            reqMap.remove(rmKey);
//            reqLock = null;
//            return true;
//        } else if (lostMap.get(passKey) != null) {
//            lostMap.remove(passKey);
//            return true;
//        } else {
//            errorTime++;
//        }
//
//        if (errorTime == 5) {
//            String rmKey = mapList.remove(0);
//            lostMap.put(rmKey, reqMap.remove(rmKey));
//            errorTime = 0; //错误次数五次就将时间放入错误，等家长使用其他
//        }
//        return false;
//    }

    public void initXml(File dest,String deviceKey) {
        // 创建DocumentBuilderFactory
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        try {
            // 创建DocumentBuilder
            DocumentBuilder builder = factory.newDocumentBuilder();

            // 创建Document
            Document document = builder.newDocument();

            // 创建根节点
            Element xml = document.createElement("xml");

//            Element host = document.createElement("host");
//            host.setTextContent(hostStr);
//
//            xml.appendChild(host);
            // 必须要有包命令因为最后传回来的数据要保存到正确的静态文件夹下
            List<Record> bagList = bagService.findByKey(deviceKey);//findAll();
            for (Record bag : bagList) {
                // 创建子节点，并设置属性
                Element bagEle = document.createElement("bag");
                bagEle.setAttribute("name", bag.get("name"));
                List<Module> modList = moduleService.findAll(bag.get("id").toString());
                for (Module mod : modList) {
                    int i = 1;
                    i++;
                    //mod.set("num", i).update();

                    Element modEle = document.createElement("module");
                    modEle.setAttribute("style", styleService.findById(mod.getStyle()).getName());

                    Element name = document.createElement("name");
                    name.setTextContent(mod.getTitle());
                    Element id = document.createElement("id");
                    id.setTextContent(mod.getId().toString());
                    Element customer = document.createElement("customer");
//                    String customerText = customerService.findById(mod.getCustomer()).getName();
//                    customer.setTextContent(customerText);
                    Element path = document.createElement("path");
                    PathConfig pathConfig = pathService.findById(mod.getPath());
//                    if (customerText.equals("sql")) {
//                        path.setTextContent(pathConfig.getPath());
//                        path.setAttribute("user", pathConfig.getUsername());
//                        path.setAttribute("password", pathConfig.getPassword());
//                        path.setAttribute("host", pathConfig.getHost());
//                        path.setAttribute("port", String.valueOf(pathConfig.getPort()));
//                        path.setAttribute("db", pathConfig.getDb());
//                    }
                    path.setTextContent(pathConfig.getPath());
                    Element ioModule = document.createElement("io_m");
                    ioModule.setTextContent(mod.getIoM().toString());
                    Element num = document.createElement("num");
                    num.setTextContent(mod.getNum().toString());
                    Element request = document.createElement("request");
                    int request_id = mod.getRequest();
                    if (request_id != 0) {
                        Request r = requestService.finadById(request_id);
                        request.setAttribute("key", mod.getRequestKey());
                        request.setAttribute("value", r.getValueType());
                        request.setAttribute("customer", customerService.findById(r.getCustomer()).getName());
                        request.setTextContent(r.getValueType());
                    } else {
                        request.setTextContent("Null");
                    }

                    Element type = document.createElement("type");
                    type.setTextContent(typeService.findById(mod.getType()).getName());


                    Element commap = document.createElement("commap");
                    List<Command> comList = comService.findByModule(mod.getId().toString());
                    for (Command com : comList) {
                        Element command = document.createElement("command");

                        Element key = document.createElement("key");
                        key.setTextContent(com.getLable());
                        Element value = document.createElement("value");
                        value.setTextContent(com.getValue());
                        Element active = document.createElement("active");
                        active.setTextContent(activeService.findById(com.getActive()).getName());

                        command.appendChild(key);
                        command.appendChild(value);
                        command.appendChild(active);

                        commap.appendChild(command);
                    }
                    modEle.appendChild(commap);

                    modEle.appendChild(name);
                    modEle.appendChild(id);
                    modEle.appendChild(customer);
                    modEle.appendChild(path);
                    modEle.appendChild(commap);
                    modEle.appendChild(ioModule);
                    modEle.appendChild(num);
                    modEle.appendChild(request);
                    modEle.appendChild(type);

                    bagEle.appendChild(modEle);
                }
                // 为根节点添加子节点
                xml.appendChild(bagEle);
            }

            // 将根节点添加到Document下
            document.appendChild(xml);

            // 创建TransformerFactory对象
            TransformerFactory tff = TransformerFactory.newInstance();

            // 创建Transformer对象
            Transformer tf = tff.newTransformer();

            // 设置输出数据时换行
            tf.setOutputProperty(OutputKeys.INDENT, "yes");

            // 使用Transformer的transform()方法将DOM树转换成XML
            tf.transform(new DOMSource(document), new StreamResult(dest));

        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerConfigurationException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        }
    }

}
